#include "crt.h"
#include "RawPartition.h"
#include <assert.h>

class TraceResult: public ITraceResult
{
public: 
	TraceResult();
	virtual ~TraceResult();

	virtual bool HitAny() const;
	virtual size_t NumberOfHits() const;
	virtual const TSceneIntersection* GetHit(size_t index) const;

	virtual size_t NumberOfRayAABBTesting() const;
	virtual size_t NumberOfRayTriangleTesting() const;

	// Total time
	//virtual double TimeOfRayAABBTesting() const;
	//virtual double TimeOfRayTriangleTesting() const;

	void AddHit(const TSceneIntersection& hit, IScenePartition::TraceType traceType);
	void AddRayTriangleTesting();

	void Reset();
	bool CanStopTrace() const;

protected:
	std::vector<TSceneIntersection> mHits;
	size_t mNumberOfRayTriangleTesting;
	bool mbCanStopTrace;
};

TraceResult::TraceResult()
{
	Reset();
}

TraceResult::~TraceResult()
{
	Reset();
}

void TraceResult::Reset()
{
	mHits.clear();
	mNumberOfRayTriangleTesting = 0;
	mbCanStopTrace = false;
}

bool TraceResult::HitAny() const
{
	return mHits.size() > 0;
}

size_t TraceResult::NumberOfHits() const
{
	return mHits.size();
}

const TSceneIntersection* TraceResult::GetHit(size_t index) const
{
	if (0 <= index && index < mHits.size())
	{
		return &mHits[index];
	}
	else
	{
		assert(0);
		return NULL;
	}
}

size_t TraceResult::NumberOfRayAABBTesting() const
{
	return 0;
}

size_t TraceResult::NumberOfRayTriangleTesting() const
{
	return mNumberOfRayTriangleTesting;
}

void TraceResult::AddHit(const TSceneIntersection& hit, IScenePartition::TraceType traceType)
{
	if (!mbCanStopTrace)
	{
		if (IScenePartition::kStopOnFirstHit == traceType)
		{
			assert(mHits.size() == 0);
			mHits.push_back(hit);
			mbCanStopTrace = true;
		}
		else if (IScenePartition::kNearestHit == traceType)
		{
			assert(mHits.size() <= 1);
			if (mHits.size() == 1)
			{
				if (mHits[0].dist > hit.dist)
				{
					mHits[0] = hit;
				}
			}
			else
			{
				mHits.push_back(hit);
			}
		}
		else // IScenePartition::kAllHits
		{
			mHits.push_back(hit);	
		}
	}
}

bool TraceResult::CanStopTrace() const
{
	return mbCanStopTrace;
}

void TraceResult::AddRayTriangleTesting()
{
	mNumberOfRayTriangleTesting++;
}

//////////////////////////////////////////////////////////////////////////

RawPartition::RawPartition()
{
	mpResult = new TraceResult;
}

RawPartition::~RawPartition()
{
	CleanMembers();
}

void RawPartition::CleanMembers()
{
	delete mpResult;
	mpResult = NULL;
	mMeshes.clear();
}

// return number of meshes in the scene
size_t RawPartition::ClearAllMeshes()
{
	mMeshes.clear();

	return 0;
}

size_t RawPartition::AddMesh(const TCRTMesh* pMesh)
{
	mMeshes.push_back(const_cast<TCRTMesh*>(pMesh));

	return mMeshes.size();
}

size_t RawPartition::GetNumberOfMeshes() const
{
	return mMeshes.size();
}

const TCRTMesh* RawPartition::GetMesh(size_t index) const
{
	if (0 <= index && index < mMeshes.size())
	{
		return mMeshes[index];
	}
	else
	{
		assert(0);
		return NULL;
	}
}

bool RawPartition::BuildSpacePartition()
{
	return true;
}

ITraceResult* RawPartition::TraceRay(const RAY* pRay, TraceType traceType)
{
	mpResult->Reset();

	for (size_t i = 0; i < mMeshes.size(); i++)
	{
		TCRTMesh* pMesh = mMeshes[i];

		for (size_t j = 0; j < pMesh->triangles.size(); j++)
		{
			TCRTTriangle& triangle = pMesh->triangles[j];
			TCRTTriangleHelper& triangleHelper = pMesh->triangleHelpers[j];

			TSceneIntersection intersection;
			bool bHit = RayIntersectTriangle(
				*pRay, 
				triangleHelper, 
				intersection.u, 
				intersection.v, 
				intersection.dist);

			mpResult->AddRayTriangleTesting();

			if (bHit)
			{
				intersection.meshIndex = i;
				intersection.index[0] = triangle.index[0];
				intersection.index[1] = triangle.index[1];
				intersection.index[2] = triangle.index[2];
				mpResult->AddHit(intersection, traceType);

				if (mpResult->CanStopTrace())
				{
					break;
				}
			}
		}

		if (mpResult->CanStopTrace())
		{
			break;
		}
	}

	return mpResult;
}